-- 工具函数

local mis_cjson	= require('cjson.safe')

local ngx = ngx
local pairs = pairs
local type = type
local loadstring = loadstring
local string_sub = string.sub
local string_find = string.find
local string_match = string.match
local string_gmatch = string.gmatch
local string_format = string.format
local table_insert = table.insert
local table_concat = table.concat
local math_floor = math.floor
local math_pow = math.pow
local math_max = math.max
local math_abs = math.abs
local tonumber = tonumber
local tostring = tostring

local mis_log = nil
local mis_calcu = nil
local mis_config = nil
local t_retCode = nil

--------------------------------------------------------------
local mis_common = {
	s_name = string.match(debug.getinfo(1).short_src, '.*/(.*).lua');	-- 模块名称
}
local this = mis_common
local t_m_dayUntilMonth = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}
local t_m_dayUntilMonth4 = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
local t_m_dayOfMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
local t_m_dayOfMonth4 = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}

--------------------------------------------------------------
-- 说明：模块包含
-- 参数：模块表
this.modules_include = function(t_moddules)
	mis_log = t_moddules.mis_log
	mis_calcu = t_moddules.mis_calcu
	mis_config = t_moddules.mis_config
	t_retCode = mis_config.t_retCode
end

--------------------------------------------------------------
-- 说明：table查找
-- 参数：t_, _cell
-- 返回：匹配位置key或nil
this.table_find = function(t_, _cell)
	if type(_cell) == 'function' then
		for _key, _v in pairs(t_) do
			if _cell(_v) then
				return _key
			else
			end
		end
	else
		for _key, _v in pairs(t_) do
			if _v == _cell then
				return _key
			end
		end
	end
	return nil
end

this.exist_table_key = function(t_, _key)
	for _k, _v in pairs(t_) do
		if _k == _key then
			return true
		end
	end
	return false
end

--------------------------------------------------------------
-- 说明：t -> s
-- 参数：t_
-- 返回：s
this.table_to_string = function(t_)
	local t_mark, t_assign = {}, {}
	local function ser_table(t_tbl, s_parent)
		local t_tmp = {}
		local s_key, s_ty, s_dotKey
		t_mark[t_tbl] = s_parent
		for sn_, _v in pairs(t_tbl) do
			s_key = type(sn_) == 'number' and string_format('[%d]', sn_) or string_format('[%q]', sn_)
			s_ty = type(_v)
			if s_ty == 'table' then
				local s_dotKey = string_format('%s%s', s_parent, s_key)
				if t_mark[_v] then
					table_insert(t_assign, string_format('%s=%s', s_dotKey, t_mark[_v]))
				else
					table_insert(t_tmp, string_format('%s=%s', s_key, ser_table(_v, s_dotKey)))
				end
			elseif s_ty == 'string' then
				table_insert(t_tmp, string_format('%s=%q', s_key, _v))
			elseif s_ty == 'number' then
				table_insert(t_tmp, string_format('%s=%d', s_key, _v))
			elseif s_ty == 'boolean' then
				table_insert(t_tmp, string_format('%s=%s', s_key, (_v and 'true' or 'false')))
			end
		end
		return string_format('{%s}', table_concat(t_tmp, ','))
	end
	return string_format('%s%s', ser_table(t_, 'ret'), table_concat(t_assign, ''))
end

--------------------------------------------------------------
-- 说明：s -> t
-- 参数：s_
-- 返回：t
this.string_to_table = function(s_)
	local f_ = loadstring('do local ret=' .. s_ .. ' return ret end')
	if f_ then
		return f_()
	end
end

--------------------------------------------------------------
-- 说明：t -> json s
-- 参数：t_
-- 返回：s
this.table_to_json = function(t_)
	local s_, s_err = mis_cjson.encode(t_)
	if not s_ then
		mis_log.error('encode json failed, ' .. s_err)
	end
	return s_
end

--------------------------------------------------------------
-- 说明：json s -> t
-- 参数：s_
-- 返回：t
this.json_to_table = function(s_)
	return mis_cjson.decode(s_)
end

--------------------------------------------------------------
-- 说明：比较两个table是否值相同
--		无法检测包含boolean类型的table
--		效率偏低, 慎用
-- 参数：t_1
--		t_2
-- 返回：b
this.table_compare = function(t_1, t_2)
	local function base_compare(t_1, t_2)
		if not t_2 then return false end
		for _k, _v in pairs(t_1) do
			if type(_v) == 'table' then
				if not base_compare(t_1[_k], t_2[_k]) then
					return false
				end
			elseif _v ~= t_2[_k] then
				return false
			end
		end
		return true
	end
	return base_compare(t_1, t_2) and base_compare(t_2, t_1)
end

--------------------------------------------------------------
-- 说明：network to host, 数据从网络格式转换到主机格式(md5校验和url解码)
-- 参数：s_s7		带解码串
--		s_s7t		校验码
-- 返回：n	 		状态码
--		s 			描述
this.data_ntoh = function(s_s7, s_s7t)
	local s_calcuS7t = string_sub(ngx.md5('s7' .. (s_s7 or '')), 7, 11)
	if s_calcuS7t ~= s_s7t then
		return t_retCode.n_AUTH_ERR, 'md5 checking failed'
	end
	return t_retCode.n_OK, mis_calcu.decode_s7Url(s_s7)
end

--------------------------------------------------------------
-- 说明：host to network, 数据从主机格式转换成网络格式(生成md5和url加密)
-- 参数：s_url		待加密串
-- 返回：s			加密串
this.data_hton = function(s_url)
	local s_url_new = s_url
	local n_pos = string_find(s_url, '?')
	if n_pos and n_pos > 10 then
		local s_u1 = string_sub(s_url, 1, n_pos)
		local s_u2 = string_sub(s_url, n_pos + 1)
		local s_s7 = mis_calcu.encode_s7Url(s_u2)
		local s_md5 = ngx.md5('s7' .. (s_s7 or ''))
		local s_s7t = string_sub(s_md5, 7, 11)
		s_url_new = s_u1 .. 's7=' .. s_s7 .. '&s7t=' .. s_s7t
	end
	return s_url_new
end

--------------------------------------------------------------
-- 说明：把北京时间转化为UNIX时间
-- 参数：s_time		格式为"1970-01-01"或者"1970-01-01 00:00:00"
-- 返回：n
this.date_to_unix = function(s_time)
	local n_year, n_month, n_day, n_hour, n_min, n_sec = string_match(s_time, '(%d+)%p(%d+)%p(%d+)%p*%s*(%d*)%p*(%d*)%p*(%d*)')
	n_year = tonumber(n_year) or 0
	n_month = tonumber(n_month) or 0
	n_day = tonumber(n_day) or 0
	n_hour = tonumber(n_hour) or 0
	n_min = tonumber(n_min) or 0
	n_sec = tonumber(n_sec) or 0
	local n_a = n_year - 1
	local n_b = n_a * 365 + math_floor(n_a / 4)
	if n_year % 4 == 0 then
		n_b = n_b + t_m_dayUntilMonth4[n_month]
	else
		n_b = n_b + t_m_dayUntilMonth[n_month]
	end
	n_b = n_b + n_day
	n_b = n_b * 86400 + n_hour * 3600 + n_min * 60 + n_sec
	return math_max(n_b - 62137008000--[[second(1970-01-01) + 8*3600]], 0)
end

--------------------------------------------------------------
-- 说明：把UNIX时间转化为北京时间
-- 参数：n_time
-- 返回：s 			格式为""1970-01-01 00:00:00"
this.unix_to_date = function(n_time)
	local n_year, n_month, n_day, n_hour, n_min, n_sec
	n_time = n_time + 28800--[[北京时区]] - 94694400--[[1970.1.1-1973.1.1]]
	n_year = 1973 + math_floor(n_time / 126230400--[[4年时间]]) * 4
	n_time = n_time % 126230400
	n_day = math_floor(n_time / 86400)
	n_year = n_year + math_floor(n_day / 365)
	n_month = 12
	if n_day <= 1095--[[3年]] then
		n_day = n_day % 365 + 1
		for k, v in ipairs(t_m_dayOfMonth) do
			if n_day <= v then
				n_month = k
				break
			end
			n_day = n_day - v
		end
	else
		n_day = n_day - 1095 + 1
		for k, v in ipairs(t_m_dayOfMonth4) do
			if n_day <= v then
				n_month = k
				break
			end
			n_day = n_day - v
		end
	end
	n_time = n_time % 86400
	n_hour = math_floor(n_time / 3600)
	n_time = n_time % 3600
	n_min = math_floor(n_time / 60)
	n_sec = n_time % 60
	return string_format('%d-%02d-%02d %02d:%02d:%02d', n_year, n_month, n_day, n_hour, n_min, n_sec)
end

--------------------------------------------------------------
-- 说明：把时钟时间转化成秒
-- 参数：s_time			格式为'00:00:00'
-- 返回：n
this.clock_to_second = function(s_time)
	local n_hour, n_min, n_sec = string_match(s_time, '(%d*)%p*(%d*)%p*(%d*)')
	n_hour = tonumber(n_hour) or 0
	n_min = tonumber(n_min) or 0
	n_sec = tonumber(n_sec) or 0
	return n_hour * 3600 + n_min * 60 + n_sec
end

--------------------------------------------------------------
-- 说明：获得北京日期
-- 返回：s		格式为"1970-01-01 00:00:00"
this.get_beijing_date = function()
	local n_year, n_month, n_day, n_hour, n_min, n_sec = string_match(ngx.utctime(), '(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)')
	n_year = tonumber(n_year)
	n_month = tonumber(n_month)
	n_day = tonumber(n_day)
	n_hour = tonumber(n_hour)
	n_min = tonumber(n_min)
	n_sec = tonumber(n_sec)
	n_hour = n_hour + 8
	if n_hour > 23 then
		n_hour = n_hour % 24
		n_day = n_day + 1
		if (n_year % 4 ~= 0 and n_day > t_m_dayOfMonth[n_month]) or (n_year % 4 == 0 and n_day > t_m_dayOfMonth4[n_month]) then
			n_day = 1
			n_month = n_month + 1
			if n_month > 12 then
				n_month = 1
				n_year = n_year + 1
			end
		end
	end
	return string_format('%d-%02d-%02d %02d:%02d:%02d', n_year, n_month, n_day, n_hour, n_min, n_sec)
end

--------------------------------------------------------------
-- 说明：计算任意两个日期的天数差
--		日期格式  1970-01-01
-- 参数：s_date1, s_date2
-- 返回：n 				天数差
this.day_offset = function(s_date1, s_date2)
	local n_year1, n_month1, n_day1 = string_match(s_date1, '(%d+)-(%d+)-(%d+)')
	n_year1 = tonumber(n_year1)
	n_month1 = tonumber(n_month1)
	n_day1 = tonumber(n_day1)

	local n_year2, n_month2, n_day2 = string_match(s_date2, '(%d+)-(%d+)-(%d+)')
	n_year2 = tonumber(n_year2)
	n_month2 = tonumber(n_month2)
	n_day2 = tonumber(n_day2)
	
	local n_a = n_year1 - 1
	local n_b = n_a * 365 + math_floor(n_a / 4)
	if n_year1 % 4 == 0 then
		n_b = n_b + t_m_dayUntilMonth4[n_month1]
	else
		n_b = n_b + t_m_dayUntilMonth[n_month1]
	end
	n_b = n_b + n_day1

	local n_x = n_year2 - 1
	local n_y = n_x * 365 + math_floor(n_x / 4)
	if n_year2 % 4 == 0 then
		n_y = n_y + t_m_dayUntilMonth4[n_month2]
	else
		n_y = n_y + t_m_dayUntilMonth[n_month2]
	end
	n_y = n_y + n_day2

	return math_abs(n_y - n_b)
end

--------------------------------------------------------------
-- 说明：计算还剩多少秒到指定的时间(以北京时间为视点)
-- 参数：n_line 			指定的时间(从0点开始到指定时间点经过的秒数)
--		n_time
-- 返回：n
this.time_to_time = function(n_line, n_time)
	local n_ = ((n_time or ngx.time()) + 28800) % 86400
	if n_ <= n_line then
		return n_line - n_
	else
		return 86400 - n_ + n_line
	end
end

--------------------------------------------------------------
-- 说明：计算还剩多少秒到下一天(以北京时间为视点)
-- 参数：n_time			基准时间
-- 返回：n
this.time_to_next_day = function(n_time)
	return 86400 - ((n_time or ngx.time()) + 28800) % 86400
end

--------------------------------------------------------------
-- 说明：计算还剩多少秒到下一周(以北京时间为视点)
-- 参数：s_time			时间(1970-01-01 00:00:00)
-- 返回：n
this.time_to_next_week = function(s_time)
	local n_year, n_month, n_day, n_hour, n_min, n_sec = string_match(s_time or ngx.utctime(), '(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)')
	n_year = tonumber(n_year)
	n_month = tonumber(n_month)
	n_day = tonumber(n_day)
	n_hour = tonumber(n_hour)
	n_min = tonumber(n_min)
	n_sec = tonumber(n_sec)
	local n_numDay = (n_year - 1) * 365 + math_floor((n_year - 1) / 4) + n_day +
		(n_year % 4 ~= 0 and t_m_dayUntilMonth[n_month] or t_m_dayUntilMonth4[n_month]) + 5
	n_hour = n_hour + 8
	if n_hour > 23 then
		n_hour = n_hour % 24
		n_numDay = n_numDay + 1
	end
	local n_week = n_numDay % 7 + 1
	local n_a = 86400 - (n_hour * 3600 + n_min * 60 + n_sec)
	local n_b = (7 - n_week) * 86400
	return n_a + n_b
end

--------------------------------------------------------------
-- 说明：计算还剩多少秒到下一月(以北京时间为视点)
-- 参数：s_time			时间(1970-01-01 00:00:00)
-- 返回：n
this.time_to_next_month = function(s_time)
	local n_year, n_month, n_day, n_hour, n_min, n_sec = string_match(s_time or ngx.utctime(), '(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)')
	n_year = tonumber(n_year)
	n_month = tonumber(n_month)
	n_day = tonumber(n_day)
	n_hour = tonumber(n_hour)
	n_min = tonumber(n_min)
	n_sec = tonumber(n_sec)
	n_hour = n_hour + 8
	if n_hour > 23 then
		-- day + 1
		n_hour = n_hour % 24
		n_day = n_day + 1
		if n_day > (n_year % 4 ~= 0 and t_m_dayOfMonth[n_month] or t_m_dayOfMonth4[n_month]) then
			-- month + 1
			n_day = 1
			n_month = n_month + 1
			if n_month > 12 then
				-- year + 1, but not required
				n_month = 1
			end
		end
	end
	local n_a = 86400 - (n_hour * 3600 + n_min * 60 + n_sec)
	local n_b = ((n_year % 4 ~= 0 and t_m_dayOfMonth[n_month] or t_m_dayOfMonth4[n_month]) - n_day) * 86400
	return n_a + n_b
end

--------------------------------------------------------------
-- 说明：计算当月多少秒(以北京时间为视点)
-- 参数：s_time			时间(1970-01-01 00:00:00)
-- 返回：n
this.month_time = function(s_time)
	local n_year, n_month, n_day, n_hour, n_min, n_sec = string_match(s_time or ngx.utctime(), '(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)')
	n_year = tonumber(n_year)
	n_month = tonumber(n_month)
	n_day = tonumber(n_day)
	n_hour = tonumber(n_hour)
	n_min = tonumber(n_min)
	n_sec = tonumber(n_sec)
	n_hour = n_hour + 8
	if n_hour > 23 then
		-- day + 1
		n_hour = n_hour % 24
		n_day = n_day + 1
		if n_day > (n_year % 4 ~= 0 and t_m_dayOfMonth[n_month] or t_m_dayOfMonth4[n_month]) then
			-- month + 1
			n_day = 1
			n_month = n_month + 1
			if n_month > 12 then
				-- year + 1, but not required
				n_month = 1
			end
		end
	end
	return ((n_year % 4 ~= 0) and t_m_dayOfMonth[n_month] or t_m_dayOfMonth4[n_month]) * 86400
end

--------------------------------------------------------------
-- 说明：计算两个UNIX时间是否在一个时间间隔内(以北京时间为视点)
-- 参数：n_time1, n_time2, n_line
-- 返回：b
this.is_another_time = function(n_time1, n_time2, n_line)
	if n_time1 < n_time2 then
		return n_time2 - n_time1 >= this.time_to_time(n_line, n_time1)
	else
		return n_time1 - n_time2 >= this.time_to_time(n_line, n_time2)
	end
end

--------------------------------------------------------------
-- 说明：计算两个UNIX时间是否是同一天(以北京时间为视点)
-- 参数：n_time1, n_time2
-- 返回：b
this.is_another_day = function(n_time1, n_time2)
	if n_time1 < n_time2 then
		return n_time2 - n_time1 >= this.time_to_next_day(n_time1)
	else
		return n_time1 - n_time2 >= this.time_to_next_day(n_time2)
	end
end

--------------------------------------------------------------
-- 说明：计算两个UNIX时间是否是同一周(以北京时间为视点)
-- 参数：n_time1, n_time2
-- 返回：b
this.is_another_week = function(n_time1, n_time2)
	if n_time1 < n_time2 then
		return n_time2 - n_time1 >= this.time_to_next_week(this.unix_to_date(n_time1))
	else
		return n_time1 - n_time2 >= this.time_to_next_week(this.unix_to_date(n_time2))
	end
end

--------------------------------------------------------------
-- 说明：把点分十进制版本号转换成十进制数
-- 返回：n
this.version_to_number = function(s_str)
	local n_out, n_tmp = 0
	for s_sub in string_gmatch(s_str, '(%d+)') do
		n_out = n_out * math_pow(10, 5) + (tonumber(s_sub) or 0)
	end
	return n_out
end

--------------------------------------------------------------
-- 说明：获取完整请求链
-- 返回：s
this.get_request_url = function()
	return ngx.var.server_addr .. ':' .. ngx.var.server_port .. ngx.var.request_uri
end

--table是空的
this.tableIsEmpty = function(t)
	if  type(t) == 'table' then
		return (next(t) == nil)
	else
		return false;  --不是table
	end
end

local get_today_zero_time = function(time)
    time = time or os.time()
    local now_date = os.date("*t", time)
    local today_time = os.time{year=now_date.year, month=now_date.month, day=now_date.day, hour=0,min=0,sec=0}
    return today_time
end


this.is_same_day = function(time1, time2)
    local day1 = get_today_zero_time(time1)
    local day2 = get_today_zero_time(time2)
    return day1 == day2
end


return this